package com.liangsui.mss.controller.system;

import cn.hutool.core.date.DateUtil;
import com.liangsui.mss.aspectj.Log;
import com.liangsui.mss.aspectj.enums.BusinessType;
import com.liangsui.mss.constants.AuthServerConstant;
import com.liangsui.mss.constants.Constants;
import com.liangsui.mss.constants.HttpStatus;
import com.liangsui.mss.domain.LoginInfo;
import com.liangsui.mss.domain.Menu;
import com.liangsui.mss.domain.SimpleUser;
import com.liangsui.mss.dto.LoginBodyDto;
import com.liangsui.mss.service.LoginInfoService;
import com.liangsui.mss.service.MenuService;
import com.liangsui.mss.utils.AddressUtils;
import com.liangsui.mss.utils.IpUtils;
import com.liangsui.mss.utils.ShiroSecurityUtils;
import com.liangsui.mss.vo.ActiverUser;
import com.liangsui.mss.vo.AjaxResult;
import com.liangsui.mss.vo.MenuTreeVo;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * LoginController
 *
 * @author zhangjiawei
 * @email 1533167396@qq.com
 * @date 2022/12/4 14:34
 */
@RestController
@Slf4j
public class LoginController {
    @Autowired
    private MenuService menuService;

    @Autowired
    private LoginInfoService loginInfoService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @PostMapping("/login/doLogin")
    @Log(title = "用户登录", businessType = BusinessType.INSERT)
    public AjaxResult login(@RequestBody @Validated LoginBodyDto loginBodyDto, HttpServletRequest httpServletRequest) {
        //获取reids中的验证码
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
        String redisCode = opsForValue.get(AuthServerConstant.SMS_CODE_PREFIX + loginBodyDto.getUsername());
        if (null == redisCode) {
            return AjaxResult.error("验证码已经过期,请重新获取");
        } else {
            String code = redisCode.split("_")[0];
            //进行比较，不考虑大小写
            if (!code.equalsIgnoreCase(loginBodyDto.getCode())) {
                log.warn("验证码错误");
                return AjaxResult.error("验证码错误");
            }
            //验证码通过删除验证码
            redisTemplate.delete(AuthServerConstant.SMS_CODE_PREFIX + loginBodyDto.getUsername());
            //进行校验
            AjaxResult ajaxResult = AjaxResult.success();
            String username = loginBodyDto.getUsername();
            String password = loginBodyDto.getPassword();
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            Subject subject = SecurityUtils.getSubject();
            //封装loginInfo对象
            LoginInfo loginInfo = createLoginInfo(httpServletRequest);
            loginInfo.setLoginAccount(loginBodyDto.getUsername());
            try {
                subject.login(token);
                Serializable webToken = subject.getSession().getId();
                // 登录状态
                loginInfo.setLoginStatus(Constants.LOGIN_SUCCESS);
                // 当前登录的名称
                loginInfo.setUserName(ShiroSecurityUtils.getCurrentUserName() != null ? ShiroSecurityUtils.getCurrentUserName() : "未知");
                loginInfo.setMsg("登录成功");
                ajaxResult.put(Constants.TOKEN, webToken);
            } catch (Exception e) {
                log.error("用户名或密码不正确", e);
                AjaxResult.error(HttpStatus.ERROR, "用户名或密码不正确");
                loginInfo.setMsg("用户名或密码不正确");
                loginInfo.setLoginStatus(Constants.LOGIN_ERROR);
            }
            //保存登录到数据库
            loginInfoService.insertLoginInfo(loginInfo);
            return ajaxResult;
        }
    }

    /**
     * 创建登录对象
     * @param httpServletRequest
     * @return
     */
    private LoginInfo createLoginInfo(HttpServletRequest httpServletRequest) {
        LoginInfo info = new LoginInfo();
        UserAgent userAgent = UserAgent.parseUserAgentString(httpServletRequest.getHeader("User-Agent"));
        //获取用户ip地址
        String ipAddr = IpUtils.getIpAddr(httpServletRequest);
        //获取操作系统
        String osName = userAgent.getOperatingSystem().getName();
        //获取浏览器类型
        String browser = userAgent.getBrowser().getName();
        //获取登录地址
        String location = AddressUtils.getRealAddressByIP(ipAddr);

        info.setIpAddr(ipAddr);
        info.setLoginLocation(location);
        info.setOs(osName);
        info.setBrowser(browser);
        info.setLoginTime(DateUtil.date());
        info.setLoginType(Constants.LOGIN_TYPE_SYSTEM);
        return info;
    }

    /**
     * 获取用户信息
     * @return
     */
    @GetMapping({"login/getInfo"})
    public AjaxResult getInfo() {
        ActiverUser activerUser = (ActiverUser) SecurityUtils.getSubject().getPrincipal();
        AjaxResult success = AjaxResult.success();
        success.put("username", activerUser.getUser().getUserName());
        success.put("picture", activerUser.getUser().getPicture());
        success.put("roles", activerUser.getRoles());
        success.put("permissions", activerUser.getPermissions());
        return success;
    }

    /**
     * 获取菜单信息
     * @return
     */
    @GetMapping({"login/menus"})
    public AjaxResult getMenus() {
        Subject subject = SecurityUtils.getSubject();
        ActiverUser activerUser = (ActiverUser) subject.getPrincipal();
        // 判断是否为超级管理员
        boolean isAdmin = activerUser.getUser().getUserType().equals(Constants.USER_ADMIN);
        SimpleUser simpleUser = null;
        if (!isAdmin) {
            // 不是
            simpleUser = new SimpleUser(activerUser.getUser().getUserId(), activerUser.getUser().getUserName());
        }
        // 根据规则查询对应的菜单数据
        List<Menu> menus = menuService.selectMenuTree(isAdmin, simpleUser);
        List<MenuTreeVo> menuVos = new ArrayList<>();
        // 组装菜单数据
        for (Menu menu : menus) {
            menuVos.add(new MenuTreeVo(menu.getMenuId().toString(), menu.getPath()));
        }
        return AjaxResult.success(menuVos);
    }

    @PostMapping({"login/logout"})
    public AjaxResult logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return AjaxResult.success("用户退出成功");
    }
}
